#include <glib.h>
#include <network_api.h>
#include <gconf/gconf-client.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <osso-ic-dbus.h>

#define CONNECTIONS_DIR "/system/osso/connectivity/IAP"
#define DEFAULT_IPV6APN "ccinternet2.t-mobile.com"
#define RESOLVCONF "/var/run/resolv.conf.gprs"

struct net_3g_ipv6_state {
  DBusGProxy *proxy_gprs;               /* global gprs interface         */
  DBusGProxy *proxy_context_properties; /* IPv6 PDP created              */
  DBusGProxy *proxy_context_actions;    /* IPv6 PDP created              */
  gchar *interface_name;                /* interface name to go with PDP */
  DBusGConnection *connection;          /* connection to dbus            */
  icd_nw_renew_fn renew_fn;
  gchar *connected_network_type;
  guint connected_network_attrs;
  gchar *connected_network_id;
  gchar *apn;
  time_t when_connected;
} private_state; /* XXX: global variable */

gchar * ver = ICD_NW_MODULE_VERSION;

/*********************************************************************
 * gconf stubs
 */
void setup_apn(GConfClient* gconf, gchar *gconf_path) {
  gboolean use_apn;
  gchar *key;

  key = g_strdup_printf("%s/use_apn", gconf_path);
  use_apn = gconf_client_get_bool(gconf, key, NULL);
  g_free(key);

  if(private_state.apn) {
    g_free(private_state.apn);
    private_state.apn = NULL;
  }
  
  if(use_apn) {
    key = g_strdup_printf("%s/gprs_accesspointname", gconf_path);
    private_state.apn = gconf_client_get_string(gconf, key, NULL);
    fprintf(stderr,"using non-default apn: %s\n",private_state.apn);
  }
  if(!use_apn || private_state.apn == NULL) {
    private_state.apn = g_strdup(DEFAULT_IPV6APN);
    fprintf(stderr,"using default apn\n");
  }
}

/*********************************************************************
 * dbus stubs
 */
static int attach(GError **error) {
  if(!dbus_g_proxy_call(private_state.proxy_gprs, "Attach", error, G_TYPE_INVALID, G_TYPE_INVALID)) {
    if(*error) {
      if(strcmp(dbus_g_error_get_name(*error),"com.nokia.csd.GPRS.Error.AlreadyAttached") == 0) {
        return 1;  // already attached, continue
      } else {
        g_printerr("Attach failed (%s): %s",dbus_g_error_get_name(*error),(*error)->message);
      }
    } else {
      g_printerr("Attach failed but did not set the error");
    }
    return 0;
  } else {
    return 1;
  }
}

static int QuickConnect(GError **error, const char *apn, const char *pdptype, 
    const char *username, const char *password, char **gprs_context) {
  if(!dbus_g_proxy_call(private_state.proxy_gprs, "QuickConnect", error,
	G_TYPE_STRING, apn, G_TYPE_STRING, pdptype,
	G_TYPE_STRING, username, G_TYPE_STRING, password, G_TYPE_INVALID,
	DBUS_TYPE_G_OBJECT_PATH, gprs_context, G_TYPE_INVALID)) {
    g_printerr("QuickConnect failed (%s): %s",dbus_g_error_get_name(*error),(*error)->message);
    return 0;
  } else {
    return 1;
  }
}

static int GetProperty(GError **error, const char *key, GValue *ret_value) {
  if(!dbus_g_proxy_call(private_state.proxy_context_properties, "Get", error, 
	G_TYPE_STRING, "com.nokia.csd.GPRS.Context", G_TYPE_STRING, key,
	G_TYPE_INVALID,
	G_TYPE_VALUE, ret_value, G_TYPE_INVALID)) {
    g_printerr("GetProperty_s(%s) failed (%s): %s",key,dbus_g_error_get_name(*error),(*error)->message);
    return 0;
  }

  return 1;
}

static int GetProperty_t(GError **error, const char *key, uint64_t *ret_value) {
  GValue value = {0};

  if(!GetProperty(error,key,&value)) {
    return 0;
  }

  if(!G_VALUE_HOLDS_UINT64(&value)) {
    g_printerr("Get(%s) returned a non-uint64?",key);
    return 0;
  }

  *ret_value = g_value_get_uint64(&value);
  return 1;
}

static int GetProperty_s(GError **error, const char *key, gchar **ret_value) {
  GValue value = {0};

  if(!GetProperty(error,key,&value)) {
    return 0;
  }

  if(!G_VALUE_HOLDS_STRING(&value)) {
    g_printerr("Get(%s) returned a non-string?",key);
    return 0;
  }

  *ret_value = g_strdup(g_value_get_string(&value));
  return 1;
}

static int signal_strength(GError **error, guchar *percent, guchar *db) {
  DBusGProxy *proxy_phone_net;
  gint unknown_number;
  gint ret_val = 1;

  *percent = 0;
  *db = 0;

  proxy_phone_net = dbus_g_proxy_new_for_name(private_state.connection, "com.nokia.phone.net", 
      "/com/nokia/phone/net", "Phone.Net");
  if (!dbus_g_proxy_call(proxy_phone_net, "get_signal_strength", error, G_TYPE_INVALID, G_TYPE_UCHAR, percent, G_TYPE_UCHAR, db, G_TYPE_INT, &unknown_number, G_TYPE_INVALID)) {
    g_printerr("signal_strength failed (%s): %s",dbus_g_error_get_name(*error),(*error)->message);
    ret_val = 0;
  }

  g_object_unref(proxy_phone_net);

  return ret_val;
}

static void on_attached(DBusGProxy *proxy, gchar *data, void *private) {
  fprintf(stderr, "Attached: %s\n", data);
}

static void on_attachfailed(DBusGProxy *proxy, guint data1, gchar *data2, void *private) {
  fprintf(stderr, "AttachFailed: %x %s\n", data1, data2);
}

/* To Consider: should this trigger anything? */
static void on_detached(DBusGProxy *proxy, void *private) {
  fprintf(stderr, "Detached\n");
}

static void on_available(DBusGProxy *proxy, void *private) {
  fprintf(stderr, "Available\n");
}

/* XXX: I believe this dbus signal is what gets sent for "no GPRS signal", but it's not making it here */
static void on_suspended(DBusGProxy *proxy, guint data1, gchar *data2, void *private) {
  fprintf(stderr, "Suspended: %x %s\n", data1, data2);
}

static void on_ctx_connected(DBusGProxy *proxy, gchar *data1, gchar *data2, gchar *data3, gchar *data4, gchar *data5, gchar *data6, void *private) {
  fprintf(stderr, "CTX Connected: %s %s %s %s %s %s\n",data1, data2, data3, data4, data5, data6);
}

static void on_ctx_connectfailed(DBusGProxy *proxy, guint data1, gchar *data2, void *private) {
  fprintf(stderr, "CTX Connect Failed: %x %s\n", data1, data2);
}

static void on_ctx_disconnected(DBusGProxy *proxy, guint data1, gchar *data2, void *private) {
  fprintf(stderr, "CTX Disconnected (calling status change fn): %x %s\n", data1, data2);
  if(private_state.when_connected > 0) {
    private_state.renew_fn(ICD_NW_LAYER_LINK,private_state.connected_network_type, private_state.connected_network_attrs, private_state.connected_network_id);
    private_state.when_connected = 0;
  }
}

static void on_ctx_deleted(DBusGProxy *proxy, void *private) {
  fprintf(stderr, "CTX Deleted - calling status change fn\n");
  if(private_state.when_connected > 0) {
    private_state.renew_fn(ICD_NW_LAYER_LINK,private_state.connected_network_type, private_state.connected_network_attrs, private_state.connected_network_id);
    private_state.when_connected = 0;
  }
}

/*********************************************************************
 * icd functions
 */
enum icd_nw_levels percent_to_level(guchar percent) {
  if(percent > 90) {
    return ICD_NW_LEVEL_10;
  } else if(percent > 80) {
    return ICD_NW_LEVEL_9;
  } else if(percent > 70) {
    return ICD_NW_LEVEL_8;
  } else if(percent > 60) {
    return ICD_NW_LEVEL_7;
  } else if(percent > 50) {
    return ICD_NW_LEVEL_6;
  } else if(percent > 40) {
    return ICD_NW_LEVEL_5;
  } else if(percent > 30) {
    return ICD_NW_LEVEL_4;
  } else if(percent > 20) {
    return ICD_NW_LEVEL_3;
  } else if(percent > 10) {
    return ICD_NW_LEVEL_2;
  } else if(percent > 4) {
    return ICD_NW_LEVEL_1;
  }

  return ICD_NW_LEVEL_NONE;
}

static void
nw_start_search_fn (const gchar *network_type,
                           guint search_scope,
                           icd_nw_search_cb_fn search_cb,
                           const gpointer search_cb_token,
                           gpointer *private) {
  GConfClient* gconf;
  GSList *dirs, *d;
  guchar percent, db;
  GError *error = NULL;

  gconf = gconf_client_get_default ();

  dirs = gconf_client_all_dirs(gconf, CONNECTIONS_DIR, NULL);
  if (dirs && g_slist_length (dirs)>0) {
    d = dirs;
    while(d) {
      gchar *n, *id, key[256], *t, *m;
      n=d->data;
      id = g_path_get_basename(n);
      g_snprintf(key, 255, "%s/type", n);
      t=gconf_client_get_string(gconf, key, NULL);
      if ( ! strcmp("GPRSv6", t)) {
	g_snprintf(key, 255, "%s/name", n);
	m=gconf_client_get_string(gconf, key, NULL);

	setup_apn(gconf,n);

	if(m) {
	  if(!signal_strength(&error, &percent, &db)) {
	    g_printerr("search: signal_strength failed");
	  } else {
	    if(percent > 1) { /* only show connection if we have a signal and name */
	      enum icd_nw_levels signal_strength;
	      signal_strength = percent_to_level(percent);

	      search_cb(ICD_NW_SEARCH_CONTINUE, m, 
		  "GPRSv6", ICD_NW_ATTR_IAPNAME | ICD_NW_ATTR_AUTOCONNECT, 
		  id, signal_strength, private_state.apn, db, search_cb_token);
	    }
	  }
	  g_free(m);
	}
      }
      g_free(id);
      g_free(t);
      d=d->next;
    }
    g_slist_foreach (dirs, (GFunc) g_free, NULL);
    g_slist_free(dirs);
  }
  g_object_unref(gconf);
  search_cb(ICD_NW_SEARCH_COMPLETE, NULL, NULL, 0,  NULL, 0, NULL, 0, search_cb_token);
}

static void ip_down (const gchar *network_type, 
		const guint network_attrs,
		const gchar *network_id,
	       	const gchar *interface_name,
	       	icd_nw_ip_down_cb_fn ip_down_cb,
	       	gpointer ip_down_cb_token, 
		gpointer *private)
{
  gchar *ifconfig_cmd = NULL;

  ifconfig_cmd = g_strdup_printf("/sbin/ifconfig %s down", private_state.interface_name);
  system(ifconfig_cmd);
  g_free(ifconfig_cmd);

  unlink(RESOLVCONF);

  ip_down_cb (ICD_NW_SUCCESS_NEXT_LAYER, ip_down_cb_token);
}

static void ip_up (const gchar *network_type, 
		const guint network_attrs,
		const gchar *network_id,
	       	const gchar *interface_name,
	       	icd_nw_ip_up_cb_fn ip_up_cb,
	       	gpointer ip_up_cb_token, 
		gpointer *private)
{
  char *ip_address = NULL;
  char *primary_dns = NULL;
  char *secondary_dns = NULL;
  gchar *resolvconf = NULL;
  gchar *ifconfig_cmd = NULL;
  GError *error = NULL;

  if(!GetProperty_s(&error,"PDNSAddress",&primary_dns)) {
    if(error) {
      ip_up_cb (ICD_NW_ERROR, error->message, ip_up_cb_token, NULL);
    } else {
      ip_up_cb (ICD_NW_ERROR, "unknown error getting interface name", ip_up_cb_token, NULL);
    }
    goto cleanup;
  }

  if(!GetProperty_s(&error,"SDNSAddress",&secondary_dns)) {
    if(error) {
      ip_up_cb (ICD_NW_ERROR, error->message, ip_up_cb_token, NULL);
    } else {
      ip_up_cb (ICD_NW_ERROR, "unknown error getting interface name", ip_up_cb_token, NULL);
    }
    goto cleanup;
  }

  if(!GetProperty_s(&error,"PDPAddress",&ip_address)) {
    if(error) {
      ip_up_cb (ICD_NW_ERROR, error->message, ip_up_cb_token, NULL);
    } else {
      ip_up_cb (ICD_NW_ERROR, "unknown error getting interface name", ip_up_cb_token, NULL);
    }
    goto cleanup;
  }

  if(strlen(ip_address) < 2 || (strlen(ip_address) != strspn(ip_address, "abcdef0123456789:"))) {
    g_printerr("ip_up: bad IPv6 address [%s]",ip_address);
    ip_up_cb (ICD_NW_ERROR, "IP Address incorrect", ip_up_cb_token, NULL);
    goto cleanup;
  }

  if(strlen(primary_dns) > 0) {
    if(strlen(secondary_dns) > 0) {
      resolvconf = g_strdup_printf("nameserver %s\nnameserver %s\n",primary_dns,secondary_dns);
    } else {
      resolvconf = g_strdup_printf("nameserver %s\n",primary_dns);
    }
    if(!g_file_set_contents(RESOLVCONF,resolvconf,strlen(resolvconf),&error)) {
      g_printerr("ip_up: unable to set %s: %s",RESOLVCONF,error->message);
      g_error_free(error);
    }
    g_free(resolvconf);
  }

  ifconfig_cmd = g_strdup_printf("/sbin/ifconfig %s %s up", private_state.interface_name, ip_address);
  system(ifconfig_cmd);
  g_free(ifconfig_cmd);

  ip_up_cb (ICD_NW_SUCCESS_NEXT_LAYER, NULL, ip_up_cb_token, NULL);

cleanup:
  if(error)
    g_error_free(error);
  g_free(ip_address);
  g_free(secondary_dns);
  g_free(primary_dns);
}

static void link_down (const gchar *network_type,
                           const guint network_attrs,
                           const gchar *network_id,
			   const gchar *interface_name,
                           icd_nw_link_down_cb_fn link_down_cb,
                           const gpointer link_down_cb_token,
                           gpointer *private)
{
  GError *error;

  if(private_state.proxy_context_actions == NULL) {
    g_printerr("Context.Disconnected failed: proxy_context_actions is null, called twice without link_up?");
    link_down_cb (ICD_NW_ERROR, link_down_cb_token);
    return;
  }

  error = NULL;
  if(!dbus_g_proxy_call(private_state.proxy_context_actions, "Disconnect", &error, 
	G_TYPE_INVALID, G_TYPE_INVALID)) {
    g_printerr("Context.Disconnect failed (%s): %s",dbus_g_error_get_name(error),error->message);
    link_down_cb (ICD_NW_ERROR, link_down_cb_token);
    g_error_free(error);
    return;
  }

  error = NULL;
  if(!dbus_g_proxy_call(private_state.proxy_context_actions, "Delete", &error, 
	G_TYPE_INVALID, G_TYPE_INVALID)) {
    g_printerr("Context.Delete failed (%s): %s",dbus_g_error_get_name(error),error->message);
    link_down_cb (ICD_NW_ERROR, link_down_cb_token);
    g_error_free(error);
    return;
  }

  dbus_g_proxy_disconnect_signal(private_state.proxy_context_actions,"Connected",G_CALLBACK(on_ctx_connected),NULL);
  dbus_g_proxy_disconnect_signal(private_state.proxy_context_actions,"ConnectFailed",G_CALLBACK(on_ctx_connectfailed),NULL);
  dbus_g_proxy_disconnect_signal(private_state.proxy_context_actions,"Disconnected",G_CALLBACK(on_ctx_disconnected),NULL);
  dbus_g_proxy_disconnect_signal(private_state.proxy_context_actions,"Deleted",G_CALLBACK(on_ctx_deleted),NULL);

  private_state.when_connected = 0;
  g_free(private_state.connected_network_type);
  private_state.connected_network_type = NULL;
  private_state.connected_network_attrs = 0;
  g_free(private_state.connected_network_id);
  private_state.connected_network_id = NULL;

  g_object_unref(private_state.proxy_context_properties);
  private_state.proxy_context_properties = NULL;
  g_object_unref(private_state.proxy_context_actions);
  private_state.proxy_context_actions = NULL;

  link_down_cb (ICD_NW_SUCCESS_NEXT_LAYER, link_down_cb_token);
}

static void link_stats(const gchar *network_type, const guint network_attrs, const gchar *network_id, 
    gpointer *private, icd_nw_link_stats_cb_fn link_stats_cb, const gpointer link_stats_cb_token) {
  uint64_t duration;
  gint signal;
  gint db;
  uint64_t rx, tx;
  GError *error = NULL;

  fprintf(stderr, "link_stats called\n");

  if(private_state.when_connected == 0) {
    link_stats_cb(link_stats_cb_token, network_type, network_attrs, network_id, 0, 0, NULL, 0, 0, 0);
    return;
  }
  
  duration = time(NULL) - private_state.when_connected;
  if(!GetProperty_t(&error,"RxBytes",&rx)) {
    g_printerr("Get RxBytes failed (%s): %s",dbus_g_error_get_name(error),error->message);
    g_error_free(error);
    rx = 0;
  }
  if(!GetProperty_t(&error,"TxBytes",&tx)) {
    g_printerr("Get TxBytes failed (%s): %s",dbus_g_error_get_name(error),error->message);
    g_error_free(error);
    tx = 0;
  }

  /* XXX: get db from dbus */
  db = -3;

  link_stats_cb(link_stats_cb_token, network_type, network_attrs, network_id, duration, signal, private_state.apn, db, rx, tx);
}

static void ip_addr_info(const char *network_type, const guint network_attrs, const gchar *network_id, gpointer *private, 
    icd_nw_ip_addr_info_cb_fn ip_addr_info_cb, const gpointer addr_info_cb_token) {
  ip_addr_info_cb(addr_info_cb_token, network_type, network_attrs, network_id, NULL, NULL, NULL, NULL, NULL, NULL);
}

static void ip_stats(const gchar *network_type, const guint network_attrs, 
    const gchar *network_id, gpointer *private,
    icd_nw_ip_stats_cb_fn ip_stats_cb, const gpointer ip_stats_cb_token) {
  uint64_t duration;
  uint64_t rx, tx;
  GError *error = NULL;

  fprintf(stderr, "ip_stats called\n");

  if(private_state.when_connected == 0) {
    ip_stats_cb(ip_stats_cb_token, network_type, network_attrs, network_id, 0, 0, 0);
    return;
  }
  
  duration = time(NULL) - private_state.when_connected;
  if(!GetProperty_t(&error,"RxBytes",&rx)) {
    g_printerr("Get RxBytes failed (%s): %s",dbus_g_error_get_name(error),error->message);
    g_error_free(error);
    rx = 0;
  }
  if(!GetProperty_t(&error,"TxBytes",&tx)) {
    g_printerr("Get TxBytes failed (%s): %s",dbus_g_error_get_name(error),error->message);
    g_error_free(error);
    tx = 0;
  }

  ip_stats_cb(ip_stats_cb_token, network_type, network_attrs, network_id, duration, rx, tx);
}

static void link_up (const gchar *network_type,
                           const guint network_attrs,
                           const gchar *network_id,
                           icd_nw_link_up_cb_fn link_up_cb,
                           const gpointer link_up_cb_token,
                           gpointer *private)
{
  GError *error = NULL;
  char *gprs_context = NULL;
  int test_socket_fd;
  
  test_socket_fd = socket(AF_INET6, SOCK_STREAM, 0);
  if(test_socket_fd < 0) {
    if(errno == EAFNOSUPPORT) {
      g_printerr("No IPv6 support in the kernel: %s\n",strerror(errno));
      link_up_cb (ICD_NW_ERROR, ICD_DBUS_ERROR_SYSTEM_ERROR, NULL, link_up_cb_token, NULL);
    } else {
      g_printerr("IPv6 test failed: %s\n",strerror(errno));
      link_up_cb (ICD_NW_ERROR, ICD_DBUS_ERROR_IAP_NOT_AVAILABLE, NULL, link_up_cb_token, NULL);
    }
    goto cleanup;
  } else {
    close(test_socket_fd);
  }

  fprintf(stderr,"before attach\n");
  if(!attach(&error)) {
    link_up_cb (ICD_NW_ERROR, dbus_g_error_get_name(error), NULL, link_up_cb_token, NULL);
    goto cleanup;
  }

  fprintf(stderr,"before QuickConnect\n");
  if(!QuickConnect(&error,private_state.apn,"IPv6","","",&gprs_context)) {
    link_up_cb (ICD_NW_ERROR, dbus_g_error_get_name(error), NULL, link_up_cb_token, NULL);
    goto cleanup;
  }
  fprintf(stderr,"after QuickConnect\n");

  private_state.when_connected = time(NULL);
  private_state.connected_network_type = g_strdup(network_type);
  private_state.connected_network_attrs = network_attrs;
  private_state.connected_network_id = g_strdup(network_id);

  private_state.proxy_context_properties = dbus_g_proxy_new_for_name(private_state.connection,"com.nokia.csd",
      gprs_context,"org.freedesktop.DBus.Properties");

  private_state.proxy_context_actions = dbus_g_proxy_new_for_name(private_state.connection,"com.nokia.csd",
      gprs_context,"com.nokia.csd.GPRS.Context");
 
  dbus_g_proxy_add_signal(private_state.proxy_context_actions,"Connected",G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_INVALID);
  dbus_g_proxy_add_signal(private_state.proxy_context_actions,"ConnectFailed",G_TYPE_UINT,G_TYPE_STRING,G_TYPE_INVALID);
  dbus_g_proxy_add_signal(private_state.proxy_context_actions,"Disconnected",G_TYPE_UINT,G_TYPE_STRING,G_TYPE_INVALID);
  dbus_g_proxy_add_signal(private_state.proxy_context_actions,"Deleted",G_TYPE_INVALID);

  dbus_g_proxy_connect_signal(private_state.proxy_context_actions,"Connected",G_CALLBACK(on_ctx_connected),NULL,NULL);
  dbus_g_proxy_connect_signal(private_state.proxy_context_actions,"ConnectFailed",G_CALLBACK(on_ctx_connectfailed),NULL,NULL);
  dbus_g_proxy_connect_signal(private_state.proxy_context_actions,"Disconnected",G_CALLBACK(on_ctx_disconnected),NULL,NULL);
  dbus_g_proxy_connect_signal(private_state.proxy_context_actions,"Deleted",G_CALLBACK(on_ctx_deleted),NULL,NULL);

  if(!GetProperty_s(&error,"NetIF",&(private_state.interface_name))) {
    if(error) {
      link_up_cb (ICD_NW_ERROR, error->message, NULL, link_up_cb_token, NULL);
    } else {
      link_up_cb (ICD_NW_ERROR, "unknown error getting interface name", NULL, link_up_cb_token, NULL);
    }
    goto cleanup;
  }

  link_up_cb (ICD_NW_SUCCESS_NEXT_LAYER, NULL, private_state.interface_name, link_up_cb_token, NULL);

cleanup:
  if(error)
    g_error_free(error);
  g_free(gprs_context);
}

gboolean icd_nw_init (struct icd_nw_api *network_api, 
		icd_nw_watch_pid_fn watch_fn, 
		gpointer watch_fn_token, 
		icd_nw_close_fn close_fn, 
		icd_nw_status_change_fn status_change_fn, 
		icd_nw_renew_fn renew_fn) {
  GError *error = NULL;

  private_state.proxy_context_properties = NULL;
  private_state.proxy_context_actions = NULL;
  private_state.interface_name = NULL;
  private_state.when_connected = 0;
  private_state.renew_fn = renew_fn;
  private_state.connected_network_type = NULL;
  private_state.connected_network_attrs = 0;
  private_state.connected_network_id = NULL;
  private_state.apn = NULL;

  private_state.connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
  if(private_state.connection == NULL) {
    g_printerr("dbus_g_bus_get failed: %s",error->message);
    g_error_free(error);
    return FALSE;
  }

  private_state.proxy_gprs = dbus_g_proxy_new_for_name(private_state.connection,"com.nokia.csd.GPRS",
      "/com/nokia/csd/gprs","com.nokia.csd.GPRS");

  dbus_g_proxy_add_signal(private_state.proxy_gprs,"Attached",G_TYPE_STRING,G_TYPE_INVALID);
  dbus_g_proxy_add_signal(private_state.proxy_gprs,"AttachFailed",G_TYPE_UINT,G_TYPE_STRING,G_TYPE_INVALID);
  dbus_g_proxy_add_signal(private_state.proxy_gprs,"Detached",G_TYPE_INVALID);
  dbus_g_proxy_add_signal(private_state.proxy_gprs,"Available",G_TYPE_INVALID);
  dbus_g_proxy_add_signal(private_state.proxy_gprs,"Suspended",G_TYPE_UINT,G_TYPE_STRING,G_TYPE_INVALID);

  dbus_g_proxy_connect_signal(private_state.proxy_gprs,"Attached",G_CALLBACK(on_attached),NULL,NULL);
  dbus_g_proxy_connect_signal(private_state.proxy_gprs,"AttachFailed",G_CALLBACK(on_attachfailed),NULL,NULL);
  dbus_g_proxy_connect_signal(private_state.proxy_gprs,"Detached",G_CALLBACK(on_detached),NULL,NULL);
  dbus_g_proxy_connect_signal(private_state.proxy_gprs,"Available",G_CALLBACK(on_available),NULL,NULL);
  dbus_g_proxy_connect_signal(private_state.proxy_gprs,"Suspended",G_CALLBACK(on_suspended),NULL,NULL);

  network_api->version = ver;
  network_api->private = (void *)0x12345678;
  network_api->ip_down = ip_down;
  network_api->ip_up = ip_up;
  network_api->ip_addr_info = ip_addr_info;
  network_api->ip_stats = ip_stats;
  network_api->link_pre_down = NULL;
  network_api->link_post_up = NULL;
  network_api->link_post_stats = NULL;
  network_api->link_down = link_down;
  network_api->link_up = link_up;
  network_api->link_stats = link_stats;
  network_api->search_lifetime = 31;
  network_api->search_interval = 30;
  network_api->start_search = nw_start_search_fn;
  network_api->stop_search = NULL;
  network_api->child_exit = NULL;
  network_api->network_destruct = NULL;
  network_api->ip_renew = NULL;
  network_api->link_post_renew = NULL;
  network_api->link_renew = NULL;

  return TRUE;
}
